package org.hertsig.magiccardmarket;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.hertsig.http.ConnectionHelper;
import org.hertsig.magiccardmarket.entities.Wants;
import org.hertsig.magiccardmarket.entities.Wants.Want;
import org.hertsig.magiccardmarket.entities.WantsLists;
import org.hertsig.magiccardmarket.entities.WantsLists.WantsList;
import org.hertsig.magiccardmarket.entities.out.ChangeWantsList;
import org.hertsig.magiccardmarket.entities.out.ChangeWantsList.WantslistItem;
import org.hertsig.magiccardmarket.entities.out.CreateWantsList;
import org.hertsig.magiccardmarket.xml.XmlMarshaller;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

@Slf4j
public class WantsListApi extends ApiBase {
	public WantsListApi(MagicCardMarketApi api) {
		super(api);
	}
	
	public Iterable<WantsList> wantsLists() {
		return XmlMarshaller.unmarshal(api.executeAndGet(new HttpGet(api.wantslist())), WantsLists.class);
	}
	
	public Iterable<Want> wantsList(String id) {
		return XmlMarshaller.unmarshal(api.executeAndGet(new HttpGet(api.wantslist(id))), Wants.class);
	}
	
	/**
	 * Create wants list with the given name. Names must be unique (case insensitive), even between games.
	 * If a list with the given name already exists, no list is created, but the exististing list with that name is returned.
	 */
	@SneakyThrows(IOException.class)
	public WantsList createWantsList(int gameId, String name) {
		checkNotNull(name);
		
		CreateWantsList toCreate = new CreateWantsList(ImmutableList.of(new CreateWantsList.WantsList(gameId, name)));
		HttpPost post = new HttpPost(api.wantslist());
		HttpResponse response = setEntityAndExecute(post, toCreate);
		
		WantsLists lists = XmlMarshaller.unmarshal(response.getEntity().getContent(), WantsLists.class);
		for (WantsList list : lists) {
			if (name.equalsIgnoreCase(list.getName())) {
				return list;
			}
		}
		throw new IllegalStateException("Returned lists did not include list with name " + name);
	}
	
	/**
	 * Deletes the wants list with the specified id. Wants lists can only be deleted if they are empty.
	 * @return true if the deletion was successful, false if it was not (possibly because the wants list was not empty).
	 * @throws IllegalStateException if the response code was something other than {@link ConnectionHelper#HTTP_OK} or {@link ConnectionHelper#HTTP_BAD_REQUEST}
	 */
	public boolean deleteWantsList(String wantslistId) {
		checkNotNull(wantslistId, "wantslistId");
		
		HttpDelete delete = new HttpDelete(api.wantslist(wantslistId));
		HttpResponse response = api.execute(delete);
		int responseCode = response.getStatusLine().getStatusCode();
		log.debug("DELETE completed with response code {}", responseCode);
		
		if (responseCode == ConnectionHelper.HTTP_OK) {
			return true;
		}
		if (responseCode == ConnectionHelper.HTTP_BAD_REQUEST) {
			return false;
		}
		throw new IllegalStateException("Unexpected response code " + responseCode);
	}
	
	public Iterable<Want> addToWantsList(String wantslistId, ChangeWantsList.Product... products) {
		return addToWantsList(wantslistId, ImmutableList.copyOf(products), null);
	}
	
	public Iterable<Want> addToWantsList(String wantslistId, ChangeWantsList.Metaproduct... products) {
		return addToWantsList(wantslistId, null, ImmutableList.copyOf(products));
	}
	
	@SneakyThrows(IOException.class)
	public Iterable<Want> addToWantsList(String wantslistId, List<ChangeWantsList.Product> products, List<ChangeWantsList.Metaproduct> metaproducts) {
		checkNotNull(wantslistId, "wantslistId");
		checkArgument(check(products) || check(metaproducts), "Must specify at least one product or metaproduct");
		
		ChangeWantsList toChange = new ChangeWantsList("add", products, metaproducts, null);
		HttpPut put = new HttpPut(api.wantslist(wantslistId));
		HttpResponse response = setEntityAndExecute(put, toChange);
		return XmlMarshaller.unmarshal(response.getEntity().getContent(), Wants.class);
	}
	
	public Iterable<Want> editWantsList(String wantslistId, ChangeWantsList.Want... wants) {
		return editWantsList(wantslistId, ImmutableList.copyOf(wants));
	}
	
	@SneakyThrows(IOException.class)
	public Iterable<Want> editWantsList(String wantslistId, List<ChangeWantsList.Want> wants) {
		checkNotNull(wantslistId, "wantslistId");
		checkArgument(check(wants), "Must specify at least one want");
		
		ChangeWantsList toChange = new ChangeWantsList("edit", null, null, wants);
		HttpPut put = new HttpPut(api.wantslist(wantslistId));
		HttpResponse response = setEntityAndExecute(put, toChange);
		return XmlMarshaller.unmarshal(response.getEntity().getContent(), Wants.class);
	}
	
	public Iterable<Want> deleteFromWantsList(String wantslistId, String... wantIds) {
		return deleteFromWantsList(wantslistId, Arrays.asList(wantIds));
	}
	
	@SneakyThrows(IOException.class)
	public Iterable<Want> deleteFromWantsList(String wantslistId, Iterable<String> wantIds) {
		checkNotNull(wantslistId, "wantslistId");
		checkArgument(Iterables.size(wantIds) > 0, "At least one want id required");
		
		List<ChangeWantsList.Want> wants = Lists.newArrayList();
		for (String id : wantIds) {
			wants.add(ChangeWantsList.Want.builder().idWant(id).build());
		}
		ChangeWantsList toChange = new ChangeWantsList("remove", null, null, wants);
		HttpPut put = new HttpPut(api.wantslist(wantslistId));
		HttpResponse response = setEntityAndExecute(put, toChange);
		return XmlMarshaller.unmarshal(response.getEntity().getContent(), Wants.class);
	}
	
	private boolean check(List<? extends ChangeWantsList.WantslistItem> list) {
		if (list == null || list.isEmpty()) {
			return false;
		}
		
		for (WantslistItem item : list) {
			checkNotNull(item, "List of items contains a null value");
			checkNotNull(item.getId(), "Id for a %s", item.getClass().getSimpleName());
			if (!(item instanceof ChangeWantsList.Want)) {
				checkNotNull(item.getMinCondition(), "Min condition for %s %s", item.getClass().getSimpleName(), item.getId());
				checkNotNull(item.getAmount(), "Amount for %s %s", item.getClass().getSimpleName(), item.getId());
				checkArgument(item.getAmount() > 0, "Amount for %s %s must be >= 1, but was %s", item.getClass().getSimpleName(), item.getId(), item.getAmount());
				if (item.getWishPrice() == null) {
					item.setWishPrice(0.0);
				}
			}
		}
		
		return true;
	}
}
